{
 "cells": [
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T07:41:47.609695Z",
     "start_time": "2025-02-26T07:41:33.997962Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 2.0.2\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.1\n",
      "torch 2.6.0+cu126\n",
      "cuda:0\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 加载数据"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T07:41:56.929706Z",
     "start_time": "2025-02-26T07:41:50.987805Z"
    }
   },
   "source": [
    "from torchvision import datasets\n",
    "from torchvision.transforms import ToTensor\n",
    "\n",
    "# fashion_mnist图像分类数据集\n",
    "train_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=True,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "test_ds = datasets.FashionMNIST(\n",
    "    root=\"data\",\n",
    "    train=False,\n",
    "    download=True,\n",
    "    transform=ToTensor()\n",
    ")\n",
    "\n",
    "# torchvision 数据集里没有提供训练集和验证集的划分\n",
    "# 当然也可以用 torch.utils.data.Dataset 实现人为划分\n",
    "# 从数据集到dataloader\n",
    "train_loader = torch.utils.data.DataLoader(train_ds, batch_size=16, shuffle=True)\n",
    "val_loader = torch.utils.data.DataLoader(test_ds, batch_size=16, shuffle=False)"
   ],
   "outputs": [],
   "execution_count": 2
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T07:42:08.944029Z",
     "start_time": "2025-02-26T07:42:02.689568Z"
    }
   },
   "source": [
    "from torchvision.transforms import Normalize\n",
    "\n",
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds: # 遍历每张图片,img.shape=[1,28,28]\n",
    "        mean += img.mean(dim=(1, 2))\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "\n",
    "print(cal_mean_std(train_ds))\n",
    "# 0.2860， 0.3205\n",
    "transforms = nn.Sequential(\n",
    "    Normalize([0.2860], [0.3205]) # 这里的均值和标准差是通过train_ds计算得到的\n",
    ")\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(tensor([0.2860]), tensor([0.3205]))\n"
     ]
    }
   ],
   "execution_count": 3
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型\n",
    "\n",
    "这里我们没有用`nn.Linear`的默认初始化，而是采用了xavier均匀分布去初始化全连接层的权重\n",
    "\n",
    "xavier初始化出自论文 《Understanding the difficulty of training deep feedforward neural networks》，适用于使用`tanh`和`sigmoid`激活函数的方法。当然，我们这里的模型采用的是`relu`激活函数，采用He初始化（何凯明初始化）会更加合适。感兴趣的同学可以自己动手修改并比对效果。\n",
    "\n",
    "|神经网络层数|初始化方式|early stop at epoch| val_loss | vla_acc|\n",
    "|-|-|-|-|-|\n",
    "|20|默认|\n",
    "|20|xaviier_uniform|\n",
    "|20|he_uniform|\n",
    "|...|\n",
    "\n",
    "He初始化出自论文 《Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification》"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T07:42:23.223324Z",
     "start_time": "2025-02-26T07:42:23.188504Z"
    }
   },
   "source": [
    "class NeuralNetwork(nn.Module):\n",
    "    def __init__(self, layers_num=2):\n",
    "        super().__init__()\n",
    "        self.transforms = transforms # 预处理层，标准化\n",
    "        self.flatten = nn.Flatten()\n",
    "        # 多加几层\n",
    "        self.linear_relu_stack = nn.Sequential(\n",
    "            nn.Linear(28 * 28, 100),\n",
    "            nn.ReLU(),\n",
    "        )\n",
    "        # 加19层\n",
    "        for i in range(1, layers_num):\n",
    "            self.linear_relu_stack.add_module(f\"Linear_{i}\", nn.Linear(100, 100))\n",
    "            self.linear_relu_stack.add_module(f\"relu\", nn.ReLU())\n",
    "        # 输出层\n",
    "        self.linear_relu_stack.add_module(\"Output Layer\", nn.Linear(100, 10))\n",
    "        \n",
    "        # 初始化权重\n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层的权重 W\"\"\"\n",
    "        # print('''初始化权重''')\n",
    "        for m in self.modules():\n",
    "            # print(m)\n",
    "            # print('-'*50)\n",
    "            if isinstance(m, nn.Linear):#判断m是否为全连接层\n",
    "                # https://pytorch.org/docs/stable/nn.init.html\n",
    "                nn.init.xavier_uniform_(m.weight) # xavier 均匀分布初始化权重\n",
    "                nn.init.zeros_(m.bias) # 全零初始化偏置项\n",
    "        # print('''初始化权重完成''')\n",
    "    def forward(self, x):\n",
    "        # x.shape [batch size, 1, 28, 28]\n",
    "        x = self.transforms(x) #标准化\n",
    "        x = self.flatten(x)  \n",
    "        # 展平后 x.shape [batch size, 28 * 28]\n",
    "        logits = self.linear_relu_stack(x)\n",
    "        # logits.shape [batch size, 10]\n",
    "        return logits\n",
    "total=0\n",
    "for idx, (key, value) in enumerate(NeuralNetwork(20).named_parameters()):\n",
    "    print(f\"Linear_{idx // 2:>02}\\tparamerters num: {np.prod(value.shape)}\") #np.prod是计算张量的元素个数\n",
    "    print(f\"Linear_{idx // 2:>02}\\tshape: {value.shape}\")\n",
    "    total+=np.prod(value.shape)\n",
    "total"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Linear_00\tparamerters num: 78400\n",
      "Linear_00\tshape: torch.Size([100, 784])\n",
      "Linear_00\tparamerters num: 100\n",
      "Linear_00\tshape: torch.Size([100])\n",
      "Linear_01\tparamerters num: 10000\n",
      "Linear_01\tshape: torch.Size([100, 100])\n",
      "Linear_01\tparamerters num: 100\n",
      "Linear_01\tshape: torch.Size([100])\n",
      "Linear_02\tparamerters num: 10000\n",
      "Linear_02\tshape: torch.Size([100, 100])\n",
      "Linear_02\tparamerters num: 100\n",
      "Linear_02\tshape: torch.Size([100])\n",
      "Linear_03\tparamerters num: 10000\n",
      "Linear_03\tshape: torch.Size([100, 100])\n",
      "Linear_03\tparamerters num: 100\n",
      "Linear_03\tshape: torch.Size([100])\n",
      "Linear_04\tparamerters num: 10000\n",
      "Linear_04\tshape: torch.Size([100, 100])\n",
      "Linear_04\tparamerters num: 100\n",
      "Linear_04\tshape: torch.Size([100])\n",
      "Linear_05\tparamerters num: 10000\n",
      "Linear_05\tshape: torch.Size([100, 100])\n",
      "Linear_05\tparamerters num: 100\n",
      "Linear_05\tshape: torch.Size([100])\n",
      "Linear_06\tparamerters num: 10000\n",
      "Linear_06\tshape: torch.Size([100, 100])\n",
      "Linear_06\tparamerters num: 100\n",
      "Linear_06\tshape: torch.Size([100])\n",
      "Linear_07\tparamerters num: 10000\n",
      "Linear_07\tshape: torch.Size([100, 100])\n",
      "Linear_07\tparamerters num: 100\n",
      "Linear_07\tshape: torch.Size([100])\n",
      "Linear_08\tparamerters num: 10000\n",
      "Linear_08\tshape: torch.Size([100, 100])\n",
      "Linear_08\tparamerters num: 100\n",
      "Linear_08\tshape: torch.Size([100])\n",
      "Linear_09\tparamerters num: 10000\n",
      "Linear_09\tshape: torch.Size([100, 100])\n",
      "Linear_09\tparamerters num: 100\n",
      "Linear_09\tshape: torch.Size([100])\n",
      "Linear_10\tparamerters num: 10000\n",
      "Linear_10\tshape: torch.Size([100, 100])\n",
      "Linear_10\tparamerters num: 100\n",
      "Linear_10\tshape: torch.Size([100])\n",
      "Linear_11\tparamerters num: 10000\n",
      "Linear_11\tshape: torch.Size([100, 100])\n",
      "Linear_11\tparamerters num: 100\n",
      "Linear_11\tshape: torch.Size([100])\n",
      "Linear_12\tparamerters num: 10000\n",
      "Linear_12\tshape: torch.Size([100, 100])\n",
      "Linear_12\tparamerters num: 100\n",
      "Linear_12\tshape: torch.Size([100])\n",
      "Linear_13\tparamerters num: 10000\n",
      "Linear_13\tshape: torch.Size([100, 100])\n",
      "Linear_13\tparamerters num: 100\n",
      "Linear_13\tshape: torch.Size([100])\n",
      "Linear_14\tparamerters num: 10000\n",
      "Linear_14\tshape: torch.Size([100, 100])\n",
      "Linear_14\tparamerters num: 100\n",
      "Linear_14\tshape: torch.Size([100])\n",
      "Linear_15\tparamerters num: 10000\n",
      "Linear_15\tshape: torch.Size([100, 100])\n",
      "Linear_15\tparamerters num: 100\n",
      "Linear_15\tshape: torch.Size([100])\n",
      "Linear_16\tparamerters num: 10000\n",
      "Linear_16\tshape: torch.Size([100, 100])\n",
      "Linear_16\tparamerters num: 100\n",
      "Linear_16\tshape: torch.Size([100])\n",
      "Linear_17\tparamerters num: 10000\n",
      "Linear_17\tshape: torch.Size([100, 100])\n",
      "Linear_17\tparamerters num: 100\n",
      "Linear_17\tshape: torch.Size([100])\n",
      "Linear_18\tparamerters num: 10000\n",
      "Linear_18\tshape: torch.Size([100, 100])\n",
      "Linear_18\tparamerters num: 100\n",
      "Linear_18\tshape: torch.Size([100])\n",
      "Linear_19\tparamerters num: 10000\n",
      "Linear_19\tshape: torch.Size([100, 100])\n",
      "Linear_19\tparamerters num: 100\n",
      "Linear_19\tshape: torch.Size([100])\n",
      "Linear_20\tparamerters num: 1000\n",
      "Linear_20\tshape: torch.Size([10, 100])\n",
      "Linear_20\tparamerters num: 10\n",
      "Linear_20\tshape: torch.Size([10])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "np.int64(271410)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 4
  },
  {
   "cell_type": "code",
   "source": [
    "w = torch.empty(3, 5)\n",
    "nn.init.eye_(w)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-26T07:48:59.358585Z",
     "start_time": "2025-02-26T07:48:59.348508Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[1., 0., 0., 0., 0.],\n",
       "        [0., 1., 0., 0., 0.],\n",
       "        [0., 0., 1., 0., 0.]])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 5
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T07:51:27.580743Z",
     "start_time": "2025-02-26T07:51:27.234376Z"
    }
   },
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ],
   "outputs": [],
   "execution_count": 6
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T07:51:34.072156Z",
     "start_time": "2025-02-26T07:51:29.054721Z"
    }
   },
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ],
   "outputs": [],
   "execution_count": 7
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T07:51:38.450412Z",
     "start_time": "2025-02-26T07:51:38.445065Z"
    }
   },
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 8
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T07:51:39.720119Z",
     "start_time": "2025-02-26T07:51:39.715982Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ],
   "outputs": [],
   "execution_count": 9
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T07:51:41.619864Z",
     "start_time": "2025-02-26T07:51:41.612193Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                    \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 100\n",
    "\n",
    "model = NeuralNetwork(layers_num=10)\n"
   ],
   "outputs": [],
   "execution_count": 10
  },
  {
   "cell_type": "code",
   "source": [
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用SGD\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "tensorboard_callback = TensorBoardCallback(\"runs\")\n",
    "tensorboard_callback.draw_model(model, [1, 28, 28])\n",
    "# 2. save best\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints\", save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=10, min_delta=0.001)\n",
    "\n",
    "model = model.to(device)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-26T07:51:51.935185Z",
     "start_time": "2025-02-26T07:51:51.599216Z"
    }
   },
   "outputs": [],
   "execution_count": 11
  },
  {
   "cell_type": "code",
   "source": [
    "record = training(\n",
    "    model,\n",
    "    train_loader,\n",
    "    val_loader,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_loader)\n",
    "    )"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-26T08:04:11.815410Z",
     "start_time": "2025-02-26T07:52:14.404887Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/375000 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "e5f909b15c9741e0aa474d5f6e27440f"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 30 / global_step 112500\n"
     ]
    }
   ],
   "execution_count": 12
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:04:20.422432Z",
     "start_time": "2025-02-26T08:04:20.174788Z"
    }
   },
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(6 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10000)  #横坐标是 steps"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 1200x500 with 2 Axes>"
      ],
      "image/png": "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/oFX0OunlNFOS6ToM9WRDKqJiKh0vtMbqqr0A2tpQbXtLDXXVBMREXk5J9K/c/MLcCozz+EzABERUUkYVDtZqCzBNlOdxPRvIiIi76afQC8j/ftEupb6HWgyoFaY/xTlISKiimNQXWymuuSgum70ufRvi6X0NeVERETkJUF1GTPV+nKv+MgQ1VqTiIioNAyqi81UZwIlFGKrExkMOabmW6yF6WBERETkzenf4eUWKWPqNxERlYdBtU4/W221APnagdReoMmIuAitpQZTwImIiHw7/VufqWY7LSIiKg+Dap39gdVcdgo4i5URERH5dqGycz2qOVNNRERlY1CtMwUApuAyK4AXFitjUE1EROQDM9WlB9Upeo9qBtVERFQOBtX2gpzrVZ2Uml2Te0VERETVUqgsotSH6Eu99GM/ERFRaRhUl5QCLsXKyupVnVp8zTURERH5Tvp3crqt+jfXVBMRUTkYVJc4U13ymmr9bLVevISIiIi8kH6cLyP9O9k2U830byIiKg+D6hLbapUSVLNQGRERkffTM9JKaamVkZuPzLwCdZ1BNRERlYdBtT394FpeoTK21CIiIvKBNdUlB9V6RlpEcIDaiIiIysKguhIz1XIGWzYiIiLyvfTvc6nfXE9NRETlY1Bd4kx1yUG1nK2OtJ2x5mw1ERGRF7Jay03/1ouUMfWbiIicwaDaXlDZ1b9FQmEFcAbVREREXic/F7Baypyp1rt8sJ0WERE5g0G1Pf3gWsqaalGXxcqIiIi8l/0xvpw11fEMqomIyAkMqivQUss+FYxttYiIiLyQno0WEAIYTSU+RD/Gc001ERE5g0G1vcDy07/1VLDjqdk1tVdERERUkz2qbUE107+JiMgZDKorOFNd2Kvatt6KiIiIvEg5RcpEcpp2jGf6NxEROYNBdQVaatmftWb6NxERkRevqS5lptpisSLFVv1bP5FORERUFgbVJbbUKiP923aAPc7q30RERN5Hz0YrZab6TFYezAVWdb1OBNdUExFR+RhUl9hSq/z071OZuTAX2FpyEBERkU+kf+vdPWqHByEogB+TiIiofDxalNhSq/SgOjYsCIEmA6xWICWd66qJiIh8qVBZim09td7tg4iIqDwMqktM/84o9SFGowHxkXqxMqaAExEReRV9iZdenLSUmWq20yIiImcxqK5goTJRt7ACOINqIiIiX0r/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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 13
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-26T08:04:31.786060Z",
     "start_time": "2025-02-26T08:04:29.957775Z"
    }
   },
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/best.ckpt\", map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, val_loader, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.3745\n",
      "accuracy: 0.8874\n"
     ]
    }
   ],
   "execution_count": 14
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": ""
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorch",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
